home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Users Group Library 1996 July
/
C-C++ Users Group Library July 1996.iso
/
listings
/
v_08_09
/
8n09104a
< prev
next >
Wrap
Text File
|
1990-06-23
|
13KB
|
480 lines
/*------------------------------------------------
XMODEMS.C
Author Date Description
-------------------------------------------
Jon Ward 22 Apr 90 Initial Revision.
Jon Ward 23 Apr 90 Cleanup and modify for
XMODEM-1K and XMODEM-CRC.
Jon Ward 26 Apr 90 Corrected implementation of
XMODEM-CRC.
------------------------------------------------*/
#define XMODEM_LIB 1
#include <stdio.h>
#include "xmodem.h"
#define STATIC static /* undef for debugging */
/*------------------------------------------------
Local Function Prototypes
------------------------------------------------*/
STATIC int xm_send_file (
FILE *f, /* file pointer */
xblock *xb, /* pointer to block data */
xfunc *xmf); /* xmodem external functions */
STATIC int xm_send_block (
xblock *xb, /* pointer to block data */
xfunc *xmf); /* xmodem external functions */
/*------------------------------------------------
This function sends a file via XMODEM, XMODEM-1K
or XMODEM/CRC. The f argument represents the
file to send that has been opened for reading.
------------------------------------------------*/
int xmodem_send (
int block_size, /* maximum block size */
FILE *f, /* file to write to */
int (*transmit) (char), /* xmit function */
int (*receive) (long, unsigned int *), /* recv function */
void (*dispstat) (long, long, const char *), /* display function */
int (*check_abort) (void)) /* manual abort function */
{
xblock xb; /* block data */
xfunc xmfuncs; /* xmodem external functions */
register int error_count; /* counter for errors */
register int can_count; /* cancel counter */
int error; /* gen error var */
unsigned int rerr; /* received char error */
/*------------------------------------------------
Initialize the function pointer structure.
------------------------------------------------*/
if ((xmfuncs.dispstat = dispstat) == NULL)
xmfuncs.dispstat = xm_no_disp_func;
if ((xmfuncs.check_abort = check_abort) == NULL)
xmfuncs.check_abort = xm_no_abort_func;
if ((xmfuncs.transmit = transmit) == NULL)
return (xm_perror (XERR_XMIT_FUNC, &xmfuncs));
if ((xmfuncs.receive = receive) == NULL)
return (xm_perror (XERR_RCVR_FUNC, &xmfuncs));
/*------------------------------------------------
------------------------------------------------*/
xb.total_block_count = 0L;
xb.total_byte_count = 0L;
(*xmfuncs.dispstat) (0L, 0L, "");
PURGE_RECEIVER(receive);
/*------------------------------------------------
Purge all data from the receive buffer. Then we
wait for 1 full minute. If we receive no
characters during that time, then we return
indicating the file was not transferred. If we
receive a NAK, then we begin transmission. If
we receive an excessive number of garbage
characters, then we return indicating no file
transfer.
------------------------------------------------*/
for (error_count = 0, can_count = 0; 1; )
{
int rcvd_char;
if (error_count >= START_XMIT_RETRY_COUNT)
return (xm_perror (XERR_NAK_TIMEOUT, &xmfuncs));
/* try for 1 minute */
rcvd_char = (*xmfuncs.receive) (START_XMIT_TIMEOUT, &rerr);
if (rerr != 0)
{
xm_send_cancel (xmfuncs.transmit);
return (xm_perror (XERR_CHAR_ERROR, &xmfuncs));
}
switch (rcvd_char)
{
case RECV_TIMEOUT:
xm_send_cancel (xmfuncs.transmit);
return (xm_perror (XERR_NAK_TIMEOUT, &xmfuncs));
case 'C':
xb.crc_used = 1;
break;
case NAK:
xb.crc_used = 0;
break;
case CAN:
if (++can_count >= CAN_COUNT_ABORT)
{
xm_send_cancel (xmfuncs.transmit);
return (xm_perror (XERR_RCVR_CANCEL, &xmfuncs));
}
continue;
default:
can_count = 0;
error_count++;
continue;
}
break;
}
/*------------------------------------------------
Setup the block size and the start of block
character and send the file.
------------------------------------------------*/
if (block_size == XMODEM_1K_BLOCK_SIZE)
{
xb.buflen = XMODEM_1K_BLOCK_SIZE;
xb.start_char = STX;
}
else
{
xb.buflen = XMODEM_BLOCK_SIZE;
xb.start_char = SOH;
}
if ((error = xm_send_file (f, &xb, &xmfuncs)) != XERR_OK)
return (error);
/*------------------------------------------------
Now, we send an EOT to the receiver and we
expect to get an ACK within 1 minute. If we
don't then we timeout and report an error. If
we get any character other than an ACK, we
retransmit the EOT. If we get an ACK, then we
exit with hopeful success.
------------------------------------------------*/
while (1)
{
if ((*xmfuncs.transmit) (EOT) != XMIT_OK)
return (xm_perror (XERR_OFFLINE, &xmfuncs));
switch ((*receive) (START_XMIT_TIMEOUT, &rerr))
{
case RECV_TIMEOUT:
return (xm_perror (XERR_LAST_ACK_TIMEOUT, &xmfuncs));
case ACK:
if (rerr != 0)
continue;
break;
default:
continue;
}
break;
}
return (xm_perror (XERR_OK, &xmfuncs));
}
/*------------------------------------------------
This function transmits the file associated with
the file pointer f according to the requirements
of the XMODEM transfer protocol.
------------------------------------------------*/
STATIC int xm_send_file (
FILE *f, /* file pointer */
register xblock *xb, /* pointer to block data */
register xfunc *xmf) /* xmodem external functions */
{
int done;
/*------------------------------------------------
Repeat until we have sent the whole file.
------------------------------------------------*/
for (done = 0, xb->block_num = 1; !done; xb->block_num++)
{
size_t bytes_read;
int error;
/*------------------------------------------------
Read a block of data. If it was not a full
block, then check for a file error. If it was a
file error, cancel the transfer and return an
error. Otherwise, pad the remainder of the
block with CPM EOFs.
------------------------------------------------*/
bytes_read = fread (xb->buffer, 1, xb->buflen, f);
if (bytes_read != xb->buflen)
{
register int i;
if (ferror (f))
{
xm_send_cancel (xmf->transmit);
return (xm_perror (XERR_FILE_READ, xmf));
}
for (i = bytes_read; i < xb->buflen; i++)
xb->buffer [i] = CPMEOF;
done = -1;
}
/*------------------------------------------------
Calculate the one's complement block number and
send the block.
------------------------------------------------*/
xb->not_block_num = (unsigned char) (255 - xb->block_num);
if ((error = xm_send_block (xb, xmf)) != XERR_OK)
return (error);
}
return (XERR_OK);
}
/*------------------------------------------------
This function transmits the block described by the
structure pointed to by the xb argument. The
transmit and receive arguments point to functions
that transmit and receive data to and from the
modem. A value of 0 is returned to indicate that
the block was successfully sent. A non-zero
return value indicates an offline condition.
------------------------------------------------*/
STATIC int xm_send_block (
register xblock *xb, /* pointer to block data */
register xfunc *xmf) /* xmodem external functions */
{
int error_count;
error_count = 0;
while (1)
{
int i;
int can_count; /* number of CANS received */
int block_resp; /* response to block */
unsigned rerr; /* received char error */
/*------------------------------------------------
If there were too many errors, then exit with
error.
------------------------------------------------*/
if (error_count > BLOCK_RETRY_COUNT)
{
xm_send_cancel (xmf->transmit);
return (xm_perror (XERR_ACK_TIMEOUT, xmf));
}
/*------------------------------------------------
Check for user abort and process it if
applicable.
------------------------------------------------*/
if ((*xmf->check_abort) () != 0)
{
xm_send_cancel (xmf->transmit);
return (xm_perror (XERR